package core.util;

import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

import org.apache.commons.codec.EncoderException;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.net.BCodec;

/**
 * エンコードユーティリティクラス
 *
 * @author Tadashi Nakayama
 * @version 1.0.0
 */
public final class CodecUtil {

	/**
	 * コンストラクタ
	 */
	private CodecUtil() {
		throw new AssertionError();
	}

	/**
	 * BCodecエンコード（日本語対応）
	 *
	 * @param str 対象文字列
	 * @param charset エンコード
	 * @return エンコード済文字列
	 */
	public static String encodeBCodec(final String str, final Charset charset) {
		Charset enc = charset;
		if (MojiUtil.CHARSET_JIS.equals(charset)) {
			enc = MojiUtil.CHARSET_XJIS;
		}

		try {
			String ret =  new BCodec(enc.name()).encode(MojiUtil.correctGarbled(str, charset));
			if (MojiUtil.CHARSET_JIS.equals(charset)) {
				return ret.replace(MojiUtil.CHARSET_XJIS.name(), MojiUtil.CHARSET_JIS.name());
			}
			return ret;
		} catch (EncoderException ex) {
			throw new IllegalStateException(ex);
		}
	}

	/**
	 * Base64エンコーディングする。
	 *
	 * @param str 文字データ
	 * @return 符号化文字列
	 */
	public static String encodeBase64(final String str) {
		return encodeBase64(str.getBytes(StandardCharsets.ISO_8859_1));
	}

	/**
	 * Base64エンコーディングする。
	 *
	 * @param data バイト型文字データ
	 * @return 符号化文字列
	 */
	public static String encodeBase64(final byte[] data) {
		return new String(Base64.encodeBase64(data), StandardCharsets.ISO_8859_1);
	}

	/**
	 * Base64デコーディングする。
	 *
	 * @param data 文字データ
	 * @return 復号化文字列
	 */
	public static byte[] decodeBase64(final String data) {
		return Base64.decodeBase64(data.getBytes(StandardCharsets.ISO_8859_1));
	}

	/**
	 * 引数文字列をLDAPのパスワード形式に変換する。
	 * @param str 文字データ
	 * @return 暗号化文字列
	 */
	public static String encryptPassword(final String str) {
		final String sha = "SHA";
		try {
			// SHAハッシュ化
			MessageDigest md = MessageDigest.getInstance(sha);
			md.update(str.getBytes(StandardCharsets.ISO_8859_1));
			return "{" + sha + "}" + encodeBase64(md.digest());
		} catch (NoSuchAlgorithmException ex) {
			throw new IllegalStateException(ex);
		}
	}

	/**
	 * 文字列のダイジェストを16進文字列で返却する。
	 *
	 * @param str 元文字列
	 * @return ダイジェスト
	 */
	public static String toSha1String(final String str) {
		try {
			MessageDigest md = MessageDigest.getInstance("SHA-1");
			// バイト配列からダイジェストを計算する
			md.update(str.getBytes(StandardCharsets.ISO_8859_1));
			return toStringDigest(md.digest());
		} catch (NoSuchAlgorithmException ex) {
			throw new IllegalStateException(ex);
		}
	}

	/**
	 * 文字列のダイジェストを16進文字列で返却する。
	 *
	 * @param str 元文字列
	 * @return ダイジェスト
	 */
	public static String toMd5String(final String str) {
		try {
			MessageDigest md = MessageDigest.getInstance("MD5");
			// バイト配列からダイジェストを計算する
			md.update(str.getBytes(StandardCharsets.ISO_8859_1));
			return toStringDigest(md.digest());
		} catch (NoSuchAlgorithmException ex) {
			throw new IllegalStateException(ex);
		}
	}

	/**
	 * バイト配列を文字列に変換する。
	 *
	 * @param digest バイト配列
	 * @return 変換後文字列
	 */
	private static String toStringDigest(final byte[] digest) {
		// 返却値
		StringBuilder sb = new StringBuilder();

		for (final int d : digest) {
			int n = d;
			// byte型では128から255が負値
			if (n < 0) {
				n += 256;
			}

			// 0から15は16進数で1桁
			if (n < 16) {
				sb.append("0");
			}
			sb.append(Integer.toString(n, 16));
		}

		return sb.toString();
	}
}
